'''kfol模拟计算,初稿,需要放在kfol计算器目录下使用'''

import subprocess
import re

class Kfol:

  def __init__(self, total_point = 565, weapon_id = 0, armor_id = 0,
    base_hp = 4140, step = 5, floor = 125, 
    start_point = [91, 118, 101, 253, 1, 1]):
    ## 保存参数
    self.total_point = total_point # 总点数
    self.weapon_id = weapon_id # 武器
    self.armor_id = armor_id # 护甲
    self.base_hp = base_hp # 分配点数为0时的hp数值
    self.step = step # 搜索步长
    self.floor = floor # 计算层数
    ## 计算过程使用的变量
    self.cur_point = start_point # 当前最佳点
    ## 自定义的最大寻常次数
    self.MAX_SEARCH = 10
    return
  
  # 获取一个点的胜率,返回一个%浮点数,若计算失败返回-100.0
  def getPointWinrate(self, point):
    proc = subprocess.Popen([r'kfol2_64.exe'], 
      stdin = subprocess.PIPE, 
      stdout = subprocess.PIPE
    )  
    ## Message to send to kfol2
    msg = 'b {floor} {hp} {point[0]} {point[1]} {point[2]} {point[3]} '
    msg += '{point[4]} {point[5]} {weapon} {armor}\r\n'
    msg = msg.format(
      floor = self.floor, hp = self.base_hp+20*point[1], point = point, 
      weapon = self.weapon_id, armor = self.armor_id
    )
    ## Send message(bytes) to kfol2, and get winrate
    respBytes = proc.communicate(input = msg.encode())[0]
    resp = respBytes.decode().upper()
    matches = re.findall(r'WIN RATE SUMMARY = (\d+\.\d+)', resp)
    ## Return
    if matches:
      return float(matches[0])
    return -100.0
  
  # 获取一个点的附近的点集,返回一个列表
  def getNearPoints(self, point):
    result = []
    ## 如果当前最佳点的分配已经满了,则获取所有的两项分配的一增一减点集合
    if (sum(point) == self.total_point):
      ## i是增加项,j是减少项
      for i in range(6):
        for j in range(6):
          ## 如果增加项就是减少项,或减少项已经是1不能再减少,跳过
          if (i == j) or (point[j] == 1):
            continue
          # 计算步长为指定步长,或可以变动的最大数值
          step = min(self.step, point[j]-1)
          newPoint = point.copy()
          newPoint[i] += step
          newPoint[j] -= step
          result.append(newPoint)
    ## 如果当前最佳点的分配未满,则获取所有单项增加的点
    else:
      step = min(self.step, self.total_point-sum(self.cur_point))
      for i in range(6):
        newPoint = point.copy()
        newPoint[i] += step
        result.append(newPoint)
    ## 返回
    return result
  
  ## 优化一个点,在指定点附近的点集找出最大优化,返回最大优化的点与其胜率,
  ## 返回的也可能是给定点自己,这时表示已经不能进行附近优化
  ## winrate是指定的点的胜率,如果传入可以少计算一次,
  ## 如果传入负数则表示还没有计算,需要重新算一次
  def optimizePoint(self, point, winrate = -100.0):
    ## 指定当前点与胜率
    cur_point = point
    if winrate < 0:
      cur_winrate = self.getPointWinrate(cur_point)
    else:
      cur_winrate = winrate
    ## 获取附近点集,然后计算胜率找出最大值
    nearPoints = self.getNearPoints(point)
    for near_point in nearPoints:
      near_winrate = self.getPointWinrate(near_point)
      if near_winrate > cur_winrate:
        cur_point = near_point
        cur_winrate = near_winrate
    ## 返回
    return (cur_point, cur_winrate)
    
  ## 连续寻找最大优化,不断找附近的最大优化点,
  ## 直到附近不再有优化,或次数超过指定数值.返回优化点,胜率
  def getMaxOptimizePoint(self, point):
    cur_point = point
    cur_winrate = -100.0
    for i in range(self.MAX_SEARCH):
      new_point, new_winrate = self.optimizePoint(cur_point, cur_winrate)
      print('第 {0} 次优化完成,最佳点: {1} 胜率: {2}:'.format(
        i+1, new_point, new_winrate
      ))
      if cur_point == new_point:
        cur_point, cur_winrate = new_point, new_winrate
        break
      else:
        cur_point, cur_winrate = new_point, new_winrate
    print('优化结束!')
    return cur_point, cur_winrate
       
  ## test,print NearPoints
  def _testNearPoint(self):
    nearPoints = self.getNearPoints(self.cur_point)
    for p in nearPoints:
      print(p)
    return
    
  ## test,optimizePoint
  def _testOptimizePoint(self):
    #print(self.optimizePoint(self.cur_point))
    print(self.getMaxOptimizePoint(self.cur_point))
    return
    
  ## Main
  def main(self):
    print('开始进行优化')
    print(self.getMaxOptimizePoint(kfol.cur_point))
    return
  
  
## Main script
if __name__ == '__main__':
  try:
    kfol = Kfol(step = 1)
    kfol.main()
  except Exception as e:
    print(e)
  ## Exit
  input('Press Enter to Exit')
          
    
  